/*
 *    At least 2 modules
 *    At least 3 interfaces
 *    At least 6 classes
 *    At least 2 uses of inheritance
 *    At least 12 methods
 *    At least one generic use
 *    At least one static use -> displayBatteryModel()
 *    Everything should be strongly typed
*/

module DevicesOOP {
    import IDevice = Interfaces.IDevice;
    import IMobileDevice = Interfaces.IMobileDevice;
    import IGSM = Interfaces.IGSM;
    import IProvideInfo = Interfaces.IProvideInfo;
    import BatteryModel = Enumerations.BatteryModel;

    export module Enumerations {
        export enum BatteryModel {
            LiIon,
            NiMH,
            NiCd
        }
    }

    export module Interfaces {
        export interface IDevice {
            price: number;
            serialNumber: string;
            manufacturer?: string;
        }

        export interface IMobileDevice {
            batteryModel: BatteryModel;
        }

        export interface IGSM {
            addCall(call: Software.Call): void;
            removeLastCall(): Software.Call;
            clearCallHistory(): void;
            provideCallHistoryAsString(): string; // maybe, it was better to return the array (string[]) or similar action
        }

        export interface IProvideInfo {
            provideInfoAsString(): string; // it was better to return object instead of string (return { asd: asd })
        }
    }

    export module Software {
        export class Call implements IProvideInfo {
            private _duration: number;
            private _phoneNumber: string;
            private _callTime: Date;

            public constructor(duration: number, phoneNumber: string, callTime: Date) {
                this.duration = duration;
                this.phoneNumber = phoneNumber;
                this.callTime = callTime;
            }

            private get duration() {
                return this._duration;
            }

            private set duration(duration: number) {
                if (duration <= 0) {
                    throw new Error('Call duration should be positive number');
                }

                this._duration = duration;
            }

            private get phoneNumber() {
                return this._phoneNumber;
            }

            private set phoneNumber(phoneNumber: string) {
                if (phoneNumber.length < 3) {
                    throw new Error('Phone number length should be longer than 3 digits');
                }

                this._phoneNumber = phoneNumber;
            }

            private get callTime() {
                return this._callTime;
            }

            private set callTime(callTime: Date) {
                this._callTime = callTime;
            }

            public provideInfoAsString() {
                return 'Duration: ' + this.duration + 'seconds' + '\n' +
                    'Phone number: ' + this.phoneNumber + '\n' +
                    'Time of call: ' + this.callTime;
            }
        }
    }

    export module Hardware {

        export class Battery implements IProvideInfo {
            private MAX_CAPACITY:number = 1360;

            private _model:BatteryModel;
            private _serialNumber:number;
            private _capacity:number;

            public constructor(model:BatteryModel, serialNumber:number, capacity:number) {
                this.model = model;
                this.serialNumber = serialNumber;
                this.capacity = capacity;
            }

            private get model() {
                return this._model;
            }

            private set model(model:BatteryModel) {
                this._model = model;
            }

            private get serialNumber() {
                return this._serialNumber;
            }

            private set serialNumber(serialNumber:number) {
                this._serialNumber = serialNumber;
            }

            private get capacity() {
                return this._capacity;
            }

            private set capacity(capacity:number) {
                this._capacity = capacity;
            }

            public provideInfoAsString() {
                return 'Model: ' + Battery.displayBatteryModel(this.model) + '\n' +
                    'Serial number: ' + this.serialNumber + '\n' +
                    'Hours talk: ' + this.capacity;
            }

            public static displayBatteryModel(batteryModel:BatteryModel):string {
                if (batteryModel === BatteryModel.LiIon) {
                    return 'LiIon';
                }
                else if (batteryModel === BatteryModel.NiCd) {
                    return 'NiCd';
                }
                else if (batteryModel === BatteryModel.NiMH) {
                    return 'NiMH';
                }
                else {
                    throw new Error('It is possible to display only the available battery models');
                }
            }

            private recharged():void {
                this.capacity = this.MAX_CAPACITY;
            }
        }

        export class Device implements IDevice, IProvideInfo {
            private _serialNumber:string;
            private _price:number;

            public constructor(price:number, serialNumber:string) {
                this.price = price;
                this.serialNumber = serialNumber;
            }

            get serialNumber() {
                return this._serialNumber;
            }

            set serialNumber(serialNumber:string) {
                this._serialNumber = serialNumber;
            }

            get price() {
                return this._price;
            }

            set price(price:number) {
                this._price = price;
            }

            public provideInfoAsString() : string {
                return 'Serial number: ' + this.serialNumber + '\n' +
                    'Price: ' + this.price;
            }
        }

        export class MobileDevice extends Device implements IDevice, IMobileDevice, IProvideInfo {
            private _batteryModel:BatteryModel;

            public constructor(price:number, serialNumber:string, batteryModel:BatteryModel) {
                super(price, serialNumber);

                this._batteryModel = batteryModel;
            }

            get batteryModel() {
                return this._batteryModel;
            }

            set batteryModel(batteryModel:BatteryModel) {
                this._batteryModel = batteryModel;
            }

            provideInfoAsString() : string {
                return super.provideInfoAsString() + '\n' +
                    'Battery model: ' + Battery.displayBatteryModel(this.batteryModel);
            }
        }

        export class AppleiPhone extends MobileDevice implements IDevice, IDevice, IGSM, IProvideInfo {
            private _callHistory: Software.Call[];

            public constructor(price:number, serialNumber:string, batteryModel:BatteryModel) {
                super(price, serialNumber, batteryModel);

                this._callHistory = [];
            }

            addCall(call: Software.Call): void {
                this._callHistory.push(call);
            }

            removeLastCall(): Software.Call {
                var lastCall = this._callHistory[this._callHistory.length - 1];

                this._callHistory.pop();

                return lastCall;
            }
            clearCallHistory(): void {
                this._callHistory = [];
            }

            provideCallHistoryAsString() : string {
                var infoToProvide = '';

                for (var i = 0; i < this._callHistory.length - 1; i++) {
                    infoToProvide += 'Call: ' + (i + 1) + '\n' +
                        this._callHistory[i].provideInfoAsString() + '\n'
                }

                infoToProvide += 'Call: ' + (this._callHistory.length - 1) + '\n' +
                    this._callHistory[i].provideInfoAsString();

                return infoToProvide;
            }
        }
    }

    export module GenericCollections {
        export class List<T> {
            private _collection: T[];

            add(item: T) : void {
                this._collection.push(item);
            }

            get count() : T[] {
                return this._collection;
            }
        }
    }
}